﻿//-------------------------------------------------------------------
//	Copyright (c) 2012  Zhirnov Andrey
//	This file is part of the "OGL-Tools" project.
//	See copyright notice in "ShaderSaver.h".
//-------------------------------------------------------------------

#include "gl_interceptor.h"


namespace gl
{
	using namespace UX_STL;
	using namespace UXTypes;
	using namespace UXTypesExt;
	using namespace UXLogger;
	using namespace UXFileSystem;

#	define CONST	const

	
	
/*
=================================================
	возвращает указатель на синглтон
=================================================
*/
	GLSrcFunctions * Instance()
	{
		return Singleton::Instance< GLSrcFunctions >();
	}
	
extern "C" 
{
	
// GL interceptors //
#	undef  GL_BUILDFUNC
#	define GL_BUILDFUNC( _ret, _func, _in_params, _out_params, _hook, _switch ) \
		GLAPI _ret CALLBACK _func (_in_params) \
		{\
			_hook( _out_params ); \
			return GL_CALL( _func()( _out_params ) ); \
		}
	
	GL_FUNCTIONS
	WGL_FUNCTIONS

}

	
#ifdef PLATFORM_WINDOWS
#	define GL_GETPROCADDRESS( _address )	wglGetProcAddress()( (LPCSTR)_address )
#endif

#ifdef PLATFORM_LINUX
#	define GL_GETPROCADDRESS( _address )	glXGetProcAddress()( (const GLubyte *)_address )
#endif

#ifndef GL_GETPROCADDRESS
#	error not defined "*GetProcAddress" function!
#endif

	
/*
=================================================
	конструктор
=================================================
*/
	GLSrcFunctions::GLSrcFunctions():
		_pFS(new CFileSystem()), _pLog(nullptr),
		_pInstance(nullptr), _iNumContexts(0)
	{
		// Load Libraries //
		string	s_path;
		CHECK_RETURN_R( TPlatform::GetSystemPath( s_path ), );
		s_path << "\\opengl32.dll";

		CHECK_RETURN_R( this->_sOpenGL.Load( s_path.cstr() ), );
		CHECK_RETURN_R( this->_sInteceptorLib.LoadSelf(), );


		// Init Version 1.1 //
#		undef  GL_BUILDFUNC
#		define GL_BUILDFUNC( _ret, _func, _in_params, _out_params, _hook, _switch ) \
			this->_func = (PFN##_func##PROC) this->_sOpenGL.GetProc( #_func );

		GL_FUNCTIONS
		WGL_FUNCTIONS
	}

/*
=================================================
	деструктор
=================================================
*/
	GLSrcFunctions::~GLSrcFunctions()
	{
		delete _pFS;
	}

/*
=================================================
	инициализация
=================================================
*/
	void GLSrcFunctions::Create(void *pInstance)
	{
		if ( Instance()->_pInstance == nullptr && pInstance != nullptr )
		{
			Instance()->_pInstance = pInstance;
			CHECK_RETURN_R( Instance()->_sInteceptorLib.Load( (winapi::HMODULE)pInstance ), );
		}

		if ( LOGGER() == nullptr )
		{
			CLogger *	p_logger = new CLogger();
			p_logger->Create( e_log_output::LOG_FILE_BIT |
							  e_log_output::MESSAGE_BIT, "ShaderSaver.html" );
			LOG( "GL Shader Saver initialized.", e_log_msg::DEBUG );
			Instance()->_pLog = p_logger;

			if ( Instance()->_sOpenGL.IsValid() )
				LOG( (string("Loaded OpenGL library from: \"") << Instance()->_sOpenGL.GetName() << "\".").cstr(), e_log_msg::DEBUG );

			if ( Instance()->_sInteceptorLib.IsValid() )
				LOG( (string("Loaded OpenGL interceptor from: \"") << Instance()->_sInteceptorLib.GetName() << "\".").cstr(), e_log_msg::DEBUG );
		}

		static const char	s_aOutputDir[]	= "saved_shaders";

		if ( Instance()->_pSLPrograms == nullptr )
		{
			Instance()->_sOutputDir.ClearMem();
			CHECK_RETURN_R( TPlatform::GetModulePath( Instance()->_sOutputDir ), );
			Instance()->_sOutputDir << '\\';

			if ( FILESYS()->DirectoryExist( s_aOutputDir ) ||
				 FILESYS()->NewDirectory( s_aOutputDir ) )
			{
				Instance()->_sOutputDir << s_aOutputDir << '\\';
			}

			Instance()->_pSLPrograms = nullptr;
			FILESYS()->OpenFile( (Instance()->_sOutputDir+"programs.txt").cstr(),
								  Instance()->_pSLPrograms, e_file_open_flags::DEF_WRITE );
		}
	}
	
/*
=================================================
	деинициализация
=================================================
*/
	void GLSrcFunctions::Release()
	{
		if ( LOGGER() != nullptr )
		{
			LOG( "GL Shader Saver finalized.", e_log_msg::DEBUG );
			delete (CLogger *)Instance()->_pLog;
			Instance()->_pLog = nullptr;
		}
	}

#ifdef PLATFORM_WINDOWS
/*
=================================================
	создать инстанцию и
	инициализировать функции
=================================================
*/
	HGLRC GLSrcFunctions::CreateInstance(HDC hDC)
	{
		Instance()->Create();


		// Create Context //
		HGLRC	rc = nullptr;

		CHECK_RETURN( (rc = Instance()->wglCreateContext()( hDC )) != nullptr );
		CHECK_RETURN( Instance()->wglMakeCurrent()( hDC, rc ) == winapi::TRUE );
		Instance()->_iNumContexts++;

		
		// Reinit Extensions //
#		undef  GL_BUILDFUNC
#		define GL_BUILDFUNC( _ret, _func, _in_params, _out_params, _hook, _switch ) \
			Instance()->_func = (PFN##_func##PROC) Instance()->GL_GETPROCADDRESS( #_func );

		GLEXT_FUNCTIONS
		WGLEXT_FUNCTIONS


		// write context info //
		GLint	i_major = 0;
		GLint	i_minor = 0;
		Instance()->glGetIntegerv()( GL_MAJOR_VERSION, &i_major );
		Instance()->glGetIntegerv()( GL_MINOR_VERSION, &i_minor );
		
		string	ctx_str("Created context ");
		ctx_str << "0x" << string().FormatI( (usize)rc, 16 );
		ctx_str << ", version: " << i_major << '.' << i_minor;
		LOG( ctx_str.cstr(), e_log_msg::INFO_0 );

		
		Instance()->wglMakeCurrent()( hDC, nullptr );
		return rc;
	}
	
/*
=================================================
	создать инстанцию и
	инициализировать функции
=================================================
*/
	HGLRC GLSrcFunctions::CreateInstanceExt(HDC hDC, HGLRC hSharedRC, const int *attribs)
	{
		Instance()->Create();


		// Create Context //
		HGLRC	rc = nullptr;

		CHECK_RETURN( (rc = Instance()->wglCreateContextAttribsARB()( hDC, hSharedRC, attribs )) != nullptr );
		CHECK_RETURN( Instance()->wglMakeCurrent()( hDC, rc ) == winapi::TRUE );
		Instance()->_iNumContexts++;

		
		// Reinit Extensions //
#		undef  GL_BUILDFUNC
#		define GL_BUILDFUNC( _ret, _func, _in_params, _out_params, _hook, _switch ) \
			Instance()->_func = (PFN##_func##PROC) Instance()->GL_GETPROCADDRESS( #_func );

		GLEXT_FUNCTIONS
		WGLEXT_FUNCTIONS


		// write context info //
		GLint	i_major = 0;
		GLint	i_minor = 0;
		Instance()->glGetIntegerv()( GL_MAJOR_VERSION, &i_major );
		Instance()->glGetIntegerv()( GL_MINOR_VERSION, &i_minor );

		string	ctx_str("Created context ");
		ctx_str << "0x" << string().FormatI( (usize)rc, 16 );
		ctx_str << ", version: " << i_major << '.' << i_minor;
		LOG( ctx_str.cstr(), e_log_msg::INFO_0 );

		
		Instance()->wglMakeCurrent()( hDC, nullptr );
		return rc;
	}

#else	// not PLATFORM_WINDOWS

/*
=================================================
	создать инстанцию и
	инициализировать функции
=================================================
*/
	GLXContext GLSrcFunctions::CreateInstance(Display *dpy, XVisualInfo *vis, GLXContext shareList, Bool direct)
	{
		Instance()->Create();


		// Create Context //
		GLXContext	ctx = nullptr;
		GLXDrawable drawable = Instance()->glXGetCurrentDrawable()();

		CHECK_RETURN( (ctx = Instance()->glXCreateContext()( dpy, vis, shareList, direct )) != nullptr );
		CHECK_RETURN( Instance()->glXMakeCurrent()( dpy, drawable, ctx ) );
		Instance()->_iNumContexts++;

		
		// Reinit Extensions //
#		undef  GL_BUILDFUNC
#		define GL_BUILDFUNC( _ret, _func, _in_params, _out_params, _hook, _switch ) \
			Instance()->_func = (PFN##_func##PROC) Instance()->GL_GETPROCADDRESS( #_func );

		GLEXT_FUNCTIONS
		WGLEXT_FUNCTIONS


		// write context info //
		GLint	i_major = 0;
		GLint	i_minor = 0;
		Instance()->glGetIntegerv()( GL_MAJOR_VERSION, &i_major );
		Instance()->glGetIntegerv()( GL_MINOR_VERSION, &i_minor );
		
		string	ctx_str("Created context ");
		ctx_str << "0x" << string().FormatI( (usize)ctx, 16 );
		ctx_str << ", version: " << i_major << '.' << i_minor;
		LOG( ctx_str.cstr(), e_log_msg::INFO_0 );

		
		Instance()->glXMakeCurrent()( dpy, drawable, nullptr );
		return ctx;
	}
	
/*
=================================================
	создать инстанцию и
	инициализировать функции
=================================================
*/
	GLXContext GLSrcFunctions::CreateInstanceExt(Display *dpy, GLXFBConfig config, GLXContext share_context, Bool direct, const int *attrib_list)
	{
		Instance()->Create();


		// Create Context //
		GLXContext	ctx = nullptr;
		GLXDrawable drawable = Instance()->glXGetCurrentDrawable()();

		CHECK_RETURN( (ctx = Instance()->glXCreateContextAttribsARB()( dpy, config, share_context, direct, attrib_list )) != nullptr );
		CHECK_RETURN( Instance()->glXMakeCurrent()( dpy, drawable, ctx ) );
		Instance()->_iNumContexts++;

		
		// Reinit Extensions //
#		undef  GL_BUILDFUNC
#		define GL_BUILDFUNC( _ret, _func, _in_params, _out_params, _hook, _switch ) \
			Instance()->_func = (PFN##_func##PROC) Instance()->GL_GETPROCADDRESS( #_func );

		GLEXT_FUNCTIONS
		WGLEXT_FUNCTIONS


		// write context info //
		GLint	i_major = 0;
		GLint	i_minor = 0;
		Instance()->glGetIntegerv()( GL_MAJOR_VERSION, &i_major );
		Instance()->glGetIntegerv()( GL_MINOR_VERSION, &i_minor );

		string	ctx_str("Created context ");
		ctx_str << "0x" << string().FormatI( (usize)ctx, 16 );
		ctx_str << ", version: " << i_major << '.' << i_minor;
		LOG( ctx_str.cstr(), e_log_msg::INFO_0 );

		
		Instance()->glXMakeCurrent()( dpy, drawable, nullptr );
		return ctx;
	}

#endif	// PLATFORM_WINDOWS
	
/*
=================================================
	удалить инстанцию
=================================================
*/
	void GLSrcFunctions::ReleaseInstance(void *pRC)
	{
		string	str("Deleted context 0x");
		str << string().FormatI( (usize)pRC, 16 );
		LOG( str.cstr(), e_log_msg::INFO_0 );

		if ( !( Instance()->_iNumContexts-- ) ) {
			Instance()->_pSLPrograms = nullptr;
		}
	}
	
/*
=================================================
	сохраняет исходник шейдера в отдельный файл
=================================================
*/
	void GLSrcFunctions::SaveShader(GLenum type, GLuint id, GLsizei count, const GLchar* const *str, const GLint *length)
	{
		string	s_filename;
		string	s_log;

		s_filename << Instance()->_sOutputDir;

		switch ( type )
		{
			case GL_FRAGMENT_SHADER	:			s_filename << "frag";  s_log << "Fragment";			break;
			case GL_VERTEX_SHADER :				s_filename << "vert";  s_log << "Vertex";			break;
			case GL_GEOMETRY_SHADER :			s_filename << "geom";  s_log << "Geometry";			break;
			case GL_TESS_CONTROL_SHADER :		s_filename << "cont";  s_log << "Tess Control";		break;
			case GL_TESS_EVALUATION_SHADER :	s_filename << "eval";  s_log << "Tess Evaluation";	break;
			case GL_COMPUTE_SHADER :			s_filename << "comp";  s_log << "Compute";			break;
			default :							s_filename << "unkn";  s_log << "Unknown";
		};

		s_filename << '_' << id;

		IFilePtr	p_file;

#	ifdef SAVE_ALL_SHADERS
		if ( FILESYS()->FileExist( (s_filename+".glsl").cstr() ) )
		{
			usize	i = 0;
			while ( FILESYS()->FileExist( (string(s_filename) << "_(" << i << ").glsl").cstr() ) ) { ++i; }
			s_filename << "_(" << i << ')';
		}
#	endif

		s_filename << ".glsl";

		if ( !(FILESYS()->OpenFile( s_filename.cstr(), p_file, e_file_open_flags::DEF_WRITE )) ) {
			s_log >> "Can't save " << " shader to file: \"" << s_filename << '"';
			RETURN_ERROR_R( s_log, );
		}
		s_log << "shader saved to file: \"" << s_filename << '"';

		s_filename.ClearMem();
		string &	source = s_filename;
		
		for (GLsizei i = 0; i < count; ++i)
		{
			source << (length != nullptr ? uni_c_string( str[i], length[i] ) : uni_c_string( str[i] ));

			if ( i < count-1 ) {
				source << "\n//---------------------------------------------------------\n\n";
			}
		}
		source << '\n';
		CHECK_RETURN_R( p_file->WriteS( source.cstr(), source.Length() ), );
			
		p_file = nullptr;
		LOG( s_log.cstr(), e_log_msg::INFO_EXT );
	}

/*
=================================================
	сохранить программу
=================================================
*/
	void GLSrcFunctions::SaveProgram(GLuint id, GLsizei count, const GLenum * shadertypes, const GLuint *shaders)
	{
		CHECK_VALUE_R( Instance()->_pSLPrograms != nullptr, );
		
		string	s_data;

		s_data << "Linked program " << id << " with shaders:";

		for (GLsizei i = 0; i < count; ++i)
		{
			s_data << "\n\t" << (i+1) << ": ";

			switch ( shadertypes[i] )
			{
				case GL_FRAGMENT_SHADER	:			s_data << "frag";  break;
				case GL_VERTEX_SHADER :				s_data << "vert";  break;
				case GL_GEOMETRY_SHADER :			s_data << "geom";  break;
				case GL_TESS_CONTROL_SHADER :		s_data << "cont";  break;
				case GL_TESS_EVALUATION_SHADER :	s_data << "eval";  break;
				case GL_COMPUTE_SHADER :			s_data << "comp";  break;
				default :							s_data << "unkn";
			};

			s_data << '_' << shaders[i] << ".glsl";
		}

		s_data << "\n\n";
		CHECK_RETURN_R( Instance()->_pSLPrograms->WriteS( s_data.cstr(), s_data.Length() ), );

		s_data.ClearMem();
		s_data << "Program " << id << " saved.";
		LOG( s_data.cstr(), e_log_msg::INFO_EXT );
	}
	
/*
=================================================
	сохранить программу
=================================================
*/
	void GLSrcFunctions::SaveFullProgram(GLuint id, GLsizei count, const GLenum * shadertypes, const GLuint *shaders, const string *sources)
	{
		string	s_log;
		string	s_filename;

		s_filename << Instance()->_sOutputDir << "program_" << id;

		IFilePtr	p_file;

#	ifdef SAVE_ALL_PROGRAMS
		if ( FILESYS()->FileExist( (s_filename+".prg").cstr() ) )
		{
			usize	i = 0;
			while ( FILESYS()->FileExist( (string(s_filename) << "_(" << i << ").prg").cstr() ) ) { ++i; }
			s_filename << "_(" << i << ')';
		}
#	endif
		
		s_filename << ".prg";

		if ( !(FILESYS()->OpenFile( s_filename.cstr(), p_file, e_file_open_flags::DEF_WRITE )) ) {
			s_log >> "Can't save program to file: \"" << s_filename << '"';
			RETURN_ERROR_R( s_log, );
		}
		s_log << "Program saved to file: \"" << s_filename << '"';


		s_filename.ClearMem();
		string &	s_data = s_filename;
		
		for (GLsizei i = 0; i < count; ++i)
		{
			s_data << "// [";

			switch ( shadertypes[i] )
			{
				case GL_FRAGMENT_SHADER	:			s_data << "FRAGMENT";			break;
				case GL_VERTEX_SHADER :				s_data << "VERTEX";				break;
				case GL_GEOMETRY_SHADER :			s_data << "GEOMETRY";			break;
				case GL_TESS_CONTROL_SHADER :		s_data << "TESS_CONTROL";		break;
				case GL_TESS_EVALUATION_SHADER :	s_data << "TESS_EVALUATION";	break;
				case GL_COMPUTE_SHADER :			s_data << "COMPUTE";			break;
				default :							s_data << "<unknown>";
			};

			s_data << "]\n" << sources[i] << "\n\n\n\n";
		}

		CHECK_RETURN_R( p_file->WriteS( s_data.cstr(), s_data.Length() ), );
			
		p_file = nullptr;
		LOG( s_log.cstr(), e_log_msg::INFO_EXT );
	}
	
/*
=================================================
	сохранить асемблерную программу
=================================================
*/
	void GLSrcFunctions::SaveASMProgram(GLenum type, GLuint id, GLsizei len, const GLvoid *str)
	{
		string	s_filename;
		string	s_log;
		
		s_filename << Instance()->_sOutputDir << "asm_";

		switch ( type )
		{
			case GL_FRAGMENT_PROGRAM_ARB :	s_filename << "frag";  s_log << "Fragment ";	break;
			case GL_VERTEX_PROGRAM_ARB :	s_filename << "vert";  s_log << "Vertex ";		break;
			default :						s_filename << "unkn";  s_log << "Unknown ";
		}

		s_filename << '_' << id;
		

		IFilePtr	p_file;
		
#	ifdef SAVE_ALL_SHADERS
		if ( FILESYS()->FileExist( (s_filename+".apr").cstr() ) )
		{
			usize	i = 0;
			while ( FILESYS()->FileExist( (string(s_filename) << "_(" << i << ").apr").cstr() ) ) { ++i; }
			s_filename << "_(" << i << ')';
		}
#	endif

		s_filename << ".apr";

		if ( !FILESYS()->OpenFile( s_filename.cstr(), p_file, e_file_open_flags::DEF_WRITE ) ) {
			s_log >> "Can't save" << "program to file: \"" << s_filename << '"';
			RETURN_ERROR_R( s_log, );
		}
		s_log << "program saved to file: \"" << s_filename << '"';

		s_filename.ClearMem();
		string &	source = s_filename;

		source = uni_c_string( (const char *)str, len );
		CHECK_RETURN_R( p_file->WriteS( source.cstr(), source.Length() ), );
			
		p_file = nullptr;
		LOG( s_log.cstr(), e_log_msg::INFO_EXT );
	}


#	undef  GL_BUILDFUNC
#	undef  CONST
	
	
extern "C"
{
	
#ifdef PLATFORM_WINDOWS
/*
=================================================
	
=================================================
*/
	GLAPI HGLRC CALLBACK wglCreateContextHook(HDC dc)
	{
		return GLSrcFunctions::CreateInstance( dc );
	}

	GLAPI HGLRC CALLBACK wglCreateContextAttribsARB(HDC hDC, HGLRC hShareContext, const int *attribList)
	{
		return GLSrcFunctions::CreateInstanceExt( hDC, hShareContext, attribList );
	}
	
/*
=================================================
	
=================================================
*/
	GLAPI BOOL  CALLBACK wglDeleteContextHook(HGLRC rc)
	{
		BOOL	retValue = GL_CALL( wglDeleteContext()( rc ) );
		GLSrcFunctions::ReleaseInstance( rc );
		return retValue;
	}
	
/*
=================================================
	
=================================================
*/
	GLAPI PROC  CALLBACK wglGetProcAddressHook(LPCSTR lpszProc)
	{
		PROC	srcproc	= GL_CALL( GL_GETPROCADDRESS( lpszProc ) );
		PROC	proc	= (PROC) GL_CALL( _sInteceptorLib.GetProc( lpszProc ) );

		if ( proc != nullptr ) {
			LOG( (string("set hook to \"") << lpszProc << "\" function.").cstr(), e_log_msg::INFO_EXT );
			return proc;
		}
		return srcproc;
	}

#else	// not PLATFORM_WINDOWS
	
/*
=================================================
	
=================================================
*/
	extern GLXContext glXCreateContextHook ( Display *dpy, XVisualInfo *vis, GLXContext shareList, Bool direct )
	{
		return GLSrcFunctions::CreateInstance( dpy, vis, shareList, direct );
	}
	
/*
=================================================
	
=================================================
*/
	extern GLXContext glXCreateContextAttribsARB (Display *dpy, GLXFBConfig config, GLXContext share_context, Bool direct, const int *attrib_list)
	{
		return GLSrcFunctions::CreateInstanceExt( dpy, config, share_context, direct, attrib_list );
	}
	
/*
=================================================
	
=================================================
*/
	extern void glXDestroyContextHook ( Display *dpy, GLXContext ctx )
	{
		GL_CALL( wglDeleteContext()( rc ) );
		GLSrcFunctions::ReleaseInstance( rc );
	}
	
/*
=================================================
	
=================================================
*/
	extern __GLXextFuncPtr glXGetProcAddressARBHook (const GLubyte *procName)
	{
		__GLXextFuncPtr	srcproc	= GL_CALL( GL_GETPROCADDRESS( procName ) );
		__GLXextFuncPtr	proc	= (__GLXextFuncPtr) GL_CALL( _sInteceptorLib.GetProc( procName ) );

		if ( proc != nullptr ) {
			LOG( (string("set hook to \"") << procName << "\" function.").cstr(), e_log_msg::INFO_EXT );
			return proc;
		}
		return srcproc;
	}


#endif	// PLATFORM_WINDOWS
	
/*
=================================================
	
=================================================
*/
	GLAPI void  CALLBACK glProgramStringARB(GLenum target, GLenum format, GLsizei len, const GLvoid *str)
	{
		GLint	id = 0;
		GL_CALL( glGetIntegerv()( target, &id ) );
		GL_CALL( SaveASMProgram( target, id, len, str ) );
		GL_CALL( glProgramStringARB()( target, format, len, str ) );
	}
	
/*
=================================================
	
=================================================
*/
	GLAPI void  CALLBACK glShaderSource(GLuint shader, GLsizei count, const GLchar* const *str, const GLint *length)
	{
#	ifndef DISABLE_SAVING_SHADERS
		GLint	type = 0;
		GL_CALL( glGetShaderiv()( shader, GL_SHADER_TYPE, &type ) );
		GL_CALL( SaveShader( type, shader, count, str, length ) );
#	endif
		GL_CALL( glShaderSource()( shader, count, str, length ) );
	}
	
/*
=================================================
	
=================================================
*/
	GLAPI void  CALLBACK glShaderSourceARB(GLhandleARB shader, GLsizei count, const GLcharARB ** str, const GLint *length)
	{
#	ifndef DISABLE_SAVING_SHADERS
		GLint	type = 0;
		GL_CALL( glGetObjectParameterivARB()( shader, GL_OBJECT_TYPE_ARB, &type ) );
		GL_CALL( SaveShader( type, shader, count, str, length ) );
#	endif
		GL_CALL( glShaderSourceARB()( shader, count, str, length ) );
	}
	
/*
=================================================
	
=================================================
*/
	GLAPI void  CALLBACK glLinkProgram (GLuint program)
	{
		GLsizei	i_count = 0;
		GL_CALL( glGetProgramiv()( program, GL_ATTACHED_SHADERS, &i_count ) );

		buffer<GLuint>	a_shaders( i_count );
		buffer<GLenum>	a_shadertypes( i_count );

		GL_CALL( glGetAttachedShaders()( program, a_shaders.Count(), &i_count, a_shaders.ptr() ) );
		

#	ifdef SAVE_PROGRAM_WITH_SHADERS

		buffer<string>	a_sources( i_count );
		GLint			i_length = 0;
		GLsizei			i_len	 = 0;

		FOR( i, a_shaders )
		{
			GL_CALL( glGetShaderiv()( a_shaders[i], GL_SHADER_TYPE, (GLint *)&a_shadertypes[i] ) );
			GL_CALL( glGetShaderiv()( a_shaders[i], GL_SHADER_SOURCE_LENGTH, &i_length ) );

			string &	src = a_sources[i];

			src.Reserve( i_length + 2 );
			GL_CALL( glGetShaderSource()( a_shaders[i], i_length+2, &i_len, src.ptr() ) );
			src.SetLength( i_len );
		}
		
		GL_CALL( SaveFullProgram( program, i_count, a_shadertypes.ptr(), a_shaders.ptr(), a_sources.ptr() ) );
#	else

		FOR( i, a_shaders ) {
			GL_CALL( glGetShaderiv()( a_shaders[i], GL_SHADER_TYPE, (GLint *)&a_shadertypes[i] ) );
		}

		GL_CALL( SaveProgram( program, i_count, a_shadertypes.ptr(), a_shaders.ptr() ) );
#	endif

		GL_CALL( glLinkProgram()( program ) );
	}
	
/*
=================================================
	
=================================================
*/
	GLAPI void  CALLBACK glLinkProgramARB (GLhandleARB program)
	{
		GLsizei	i_count = 0;
		GL_CALL( glGetObjectParameterivARB()( program, GL_OBJECT_ATTACHED_OBJECTS_ARB, &i_count ) );
		
		buffer<GLuint>	a_shaders( i_count );
		buffer<GLenum>	a_shadertypes( i_count );

		GL_CALL( glGetAttachedObjectsARB()( program, a_shaders.Count(), nullptr, (GLhandleARB *)a_shaders.ptr() ) );
		
#	ifdef SAVE_PROGRAM_WITH_SHADERS
		
		buffer<string>	a_sources( i_count );
		GLint			i_length = 0;
		GLsizei			i_len	 = 0;
		
		FOR( i, a_shaders )
		{
			GL_CALL( glGetObjectParameterivARB()( a_shaders[i], GL_OBJECT_TYPE_ARB, (GLint *)&a_shadertypes[i] ) );
			GL_CALL( glGetObjectParameterivARB()( a_shaders[i], GL_OBJECT_SHADER_SOURCE_LENGTH_ARB, &i_length ) );

			string &	src = a_sources[i];

			src.Reserve( i_length + 2 );
			GL_CALL( glGetShaderSourceARB()( a_shaders[i], i_length+2, &i_len, src.ptr() ) );
			src.SetLength( i_len );
		}

		GL_CALL( SaveFullProgram( program, i_count, a_shadertypes.ptr(), a_shaders.ptr(), a_sources.ptr() ) );

#	else
		FOR( i, a_shaders ) {
			GL_CALL( glGetObjectParameterivARB()( a_shaders[i], GL_OBJECT_TYPE_ARB, (GLint *)&a_shadertypes[i] ) );
		}

		GL_CALL( SaveProgram( program, i_count, a_shadertypes.ptr(), a_shaders.ptr() ) );
#	endif

		GL_CALL( glLinkProgramARB()( program ) );
	}
}


//-------------------------------------------------------------------

}	// gl

#include <logger/ux_logger_lib.h>
